/**
  ETFDAQ Project
  \class TACard
  \brief A VME card for specific data processing and acquiring functions
  Supposed to be a virtual class
  \author SUN Yazhou, asia.rabbit@163.com
  \since 2024-08-12
  \date 2024-08-12 last modified

  \copyright Copyright (c) 2024 IMP-CAS with LGPLv3 LICENSE
*/

#ifndef TACard_h
#define TACard_h

#include <cstdint>
#include <string>
#include <vector>
#include "card_type.h"
#include "TAYaml.h"

class TAController;

using std::string;
using std::vector;

class TACard{
public:
  TACard(TAController *ctrl, const TAYaml &c);
  virtual ~TACard();

  ///--- initialization ---///
  virtual void Config(); ///< config accroding to user yaml input
  void SetDebug(bool opt = true){ fDebug = opt; }
  virtual void Initialize() = 0;
  virtual void Identify() = 0; ///< read card info (e.g. serialNo.)
  virtual void SlotInit() = 0; ///< write slot id and crate id
  /// \param level 1~7, 0 will disable the IRQ generation
  /// \param vector 0x00~0xFF, requested by vme ctrler to identify the interrupter
  /// \param nEv: trigger will be generated after nof buffered events reaches nEv
  virtual void ConfigIRQ(int level, int vector, int nEv) const;

  ///--- configuration ---///
  /// to select to write the header and EOB (End of Block)
  /// anyway when there are no accepted channels
  virtual void EnableEmpty() const;
  virtual void EnableBerr() const;
  /// the following 2 methods enable or disable certain ch(s)
  /// by set the KILL bit of the ch threshold to 1
  /// \param ch starts from 0, -1 means all channels
  virtual void SetChOn(int ch = -1, bool isOn = true) const;
  virtual void SetChOff(int ch) const{ SetChOn(ch, false); }
  /// \param ch starts from 0, -1 means all channels
  virtual void SetThreshold(int ch, uint16_t thre) const;
  virtual void SetIped(int iped) const; ///< set pedestal current for qdcs
  /// \param opt e.g, for caen: true: thre*2; false(default): thre*16
  virtual void UsingSmallThre(bool opt = true) const;
  /// overflow and zero suppressions
  virtual void KeepUnderflow(bool opt = true) const;
  virtual void KeepOverflow(bool opt = true) const;
  /// data reset(DR), software reset(SR) and hardware reset(HR) are different
  /// Please refer to the register map to check what they resets respectively
  /// usually DR only resets data, ev counters and read/write pointers
  /// SR is DR plus reset of most of the controlling registers
  /// HR is SR plus all the resetable registers
  virtual void SoftwareReset() = 0;
  /// this function let the card send all data until the first EOB (end of event)
  /// then it sends no data. It is realized via setting the Control1 register
  virtual void SetReadOneEvent() const;

  ///--- the DAQ loop ---///
  virtual bool DataReady() const = 0; ///< true if buffered events are enough
  virtual bool fired(){ return DataReady(); }
  /// read all events in the buffer \retval nof words read
  virtual int ReadData(uint32_t *buf) = 0;
  /// read 1 event \retval nof words read
  virtual int ReadEvent(uint32_t *buf) = 0;
  /// clear the module's multi-event buffers, the R/W pointers, and the event counters
  virtual void ClearData() = 0; ///< data reset (DR)
  virtual void ClearEventCounter(); ///< clears the event counter -- currently not used
  /// event counter increments on all trigs (true) or only accepted trigs (false)
  virtual void SetAllTrigger(bool opt) const;
  virtual void ResetForNewRun(){}

  /// \return the event counter register
  virtual unsigned GetEventCounter() const{ return fEventCounter; }
  virtual unsigned GetDNEv() const{ return fDNEv; }
  /// \fn retrieve ev_cntr from card register
  virtual void ReadEventCounter();

  int GetSlot() const{ return fSlot; }
  const char *GetName() const{ return fCardName.data(); }
  int CardType() const{ return fCardType; }
  int SerialNo() const{ return fSerialNo; }
  virtual void Print() const;
  virtual void PrintEvent(const uint32_t *ev)  = 0; ///< print one event
  virtual void TellYouWhoIAm() const;

  /// read a uint32_t from the register via A32 addr bus
  virtual uint32_t RegReadA32D32(uint16_t addr) const;
  /// read a uint16_t from the register via A32 addr bus
  virtual uint16_t RegReadA32D16(uint16_t addr) const;
  /// read a block of uint32_t from the register via A32 addr bus, ret nget blk
  virtual int RegReadA32BLT32(uint16_t addr, uint32_t *data, uint32_t nreq) const;
  /// read a block of 2*uint32_t from the register via A32 addr bus, ret nget blk
  virtual int RegReadA32MBLT64(uint16_t addr, uint32_t *data, uint32_t nreq) const;
  /// write a uint32_t to the register via A32 addr bus
  virtual void RegWriteA32D32(uint16_t addr, uint32_t data) const;
  /// write a uint32_t to the register via A32 addr bus
  virtual void RegWriteA32D16(uint16_t addr, uint16_t data) const;

protected:
  TAYaml fConfig; ///< the user config info for the card
  int fSlot, fCrate;
  uint32_t fBaseAddress;
  TAController *fController;
  size_t fNCh; ///< nof channels in the card
  vector<uint16_t> fThres; ///< thresholds vector
  vector<bool> fChOn; ///< channel on vector
  int fIped; ///< pedestal current for QDCs
  bool fIsSmallThreshold, fIsEmptyEnable;
  bool fIsKeepUnderflow, fIsKeepOverflow;
  int fIRQLevel, fIRQVector, fEventTrigger; ///< nof buf ev > event trigger => IRQ
  long fEventCounter; ///< the event counter reg
  int fDNEv; ///< nof ev for this IRQ

  int fCardType, fSerialNo, fHardWareRev, fVersion; ///< card info from card rom
  int fFirmWareRev; ///< however this one is stored in the register
  string fCardName;
  bool fDebug;
  long fMAX_EVCTR; ///< reset ev_counter at this limit, for it may be roll over
  ECardPurpose fCardPurpose;

  /// for the differences between 16 and 32ch cards
  unsigned short fThreAddrIncre; ///< addr step for thre mem
  unsigned short fChIdBit0, fChIdMask; ///< starting bit of chid, and its mask
};

#endif
